Банки — Анализ оттока клиентов¶

Декомпозиция задачи:¶

Задачи проекта:¶

  1. Провести исследовательский анализ, включающий исследование корреляций и портретов клиентов;
  2. Проверить гипотезы (одна дана заранее, минимум ещё одну надо сформулировать), отслеживая применимость используемого стат.критерия к исследуемым данным;
  3. Сделать выводы о том, какие признаки стратегическим образом влияют на отток и какие значения или интервалы этих признаков связаны с оттоком;
  4. Выделить не мелкие, но компактные высокоотточные сегменты, приоритизировать их;
  5. Дать конкретные рекомендации по приоритетным сегментам;
  6. Подготовить презентацию со всеми ключевыми моментами.

1. Провести исследовательский анализ, включающий исследование корреляций и портретов клиентов¶

Шаг. 1 Загрузка данных и необходимых библиотек.

Шаг. 2 Знакомство и анализ данных:

  • вывод часть датафрейма на экран;
  • проверить на корректность название столбцов и данных типа str;
  • проверка нужных типов данных;
  • проверка на явные и неявные дубликаты;
  • проверка данных на пропуски;
  • построение распределений данных.

Шаг. 3 Предобработка данных:

  • Заполнение/удаление пропусков в данных, если таковые имеются:
    • оценить количество пропущенных значений (посчитать их количество и долю от всего значения);
    • изучить пропусков в данных (проанализировать, чем они могут быть вызваны);
    • попытаться выяснить можно ли их как-то заполнить (например, медианными значениями);
    • если не получается их заменить, то удалить строки с пропусками в данных, если их мало относительно всего количества данных.
  • если есть данные не подходящих типов, то привести их необходимому виду;
  • исправить названия столбцов или же данные, если необходимо;
  • проанализировать явные и неявные дубликаты, если таковые имеются:
    • оценить количество дубликатов;
    • просмотреть на явные и неявные дубликаты;
    • постараться понять природу их возникновения;
    • попытаться исправить дубликаты, если не получается, то удалить их, если их мало относительно всего количества данных.
  • проанализировать построенные графики по данным;
  • сделать выводы.

Шаг. 4 Получить основные статистические характеристики данных, проанализировать портреты клиентов и вычислить корреляцию. Визуализировать результаты, сделать выводы.

2. Проверить гипотезы (одна дана заранее, минимум ещё одну надо сформулировать), отслеживая применимость используемого стат.критерия к исследуемым данным¶

Шаг. 5 Определить статистическую значимость между определенными группами клиентов, а также между различными признаками, провести тесты, сделать выводы.

3. Сделать выводы о том, какие признаки стратегическим образом влияют на отток и какие значения или интервалы этих признаков связаны с оттоком¶

Шаг. 6 На основе полученных ранее результатах сделать выводы об основных признаков, которые влияют на отток клиентов. При необходимости провести новые тесты, определить интервалы признаков, написать полученные выводы.

4. Выделить не мелкие, но компактные высокоотточные сегменты, приоритизировать их¶

Шаг. 7 Определить высокоотточные сегменты, и притезировать их по различным метрикам (выявить наиболее валидную метрику).

5. Дать конкретные рекомендации по приоритетным сегментам¶

Шаг. 8 Детальнее проанализировать полученные сегменты, постараться эти сегменты описать, и на фоне полученного описания предложить рекомендации для получения больших денег для бизнеса (например, для определенного сегмента придумать акцию или проект по его удержанию).

6. Подготовить презентацию со всеми ключевыми моментами¶

Шаг. 9 Подготовить презентацию, куда добавить основные полученные графики и результаты, а также коротко описать результаты и сформулированные выводы, а также добавить рекомендации.

Шаг. 10 Подготовить речь для презентации и сформулировать общие выводы.

Материалы:¶

  • Презентация
  • Дашборд №1

Описание задачи и данных:¶

Датасет содержит данные о клиентах банка «Метанпром». Банк располагается в Ярославле и областных городах: Ростов Великий и Рыбинск.

Колонки:

  • user_id — идентификатор пользователя;
  • score — баллы кредитного скоринга;
  • city — город;
  • gender — пол;
  • age — возраст;
  • equity — количество баллов собственности;
  • Balance — баланс на счёте;
  • Products — количество продуктов, которыми пользуется клиент;
  • CreditCard — есть ли кредитная карта;
  • last_activity — активный клиент;
  • estimated_salary — заработная плата клиента;
  • Churn — ушёл или нет.

Обзор данных:¶

Импортируем необходимые библиотеки для работы:

In [1]:
import pandas as pd
import numpy as np
import seaborn as sns
import phik
from phik import resources, report
import matplotlib.pyplot as plt
import scipy.stats as stats
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from scipy import stats as st

Прочтем файл bank_scrooge.csv с данными и сохранение его в переменную bank_scrooge:

In [2]:
path = "https://drive.google.com/uc?export=download&id=1-U61mhTz_N1ARjy2XSAZ7IlQqGjeqP0F" 
bank_scrooge = pd.read_csv(path)

Выведем первые 5 строк датасета, ознакомимся с данными, методом info(), посмотрим на количество пропущенных значений в датасете, проверим на явные дубликаты:

In [3]:
def df_info(df):
    display(df.head())
    display(df.info())
    display(df.isna().sum())
    print('Количество явных дубликатов в датасете data:', df.duplicated().sum())

df_info(bank_scrooge)
USERID score city gender age equity balance products credit_card last_activity EST_SALARY churn
0 183012 850.0 Рыбинск Ж 25.0 1 59214.82 2 0 1 75719.14 1
1 146556 861.0 Рыбинск Ж 37.0 5 850594.33 3 1 0 86621.77 0
2 120722 892.0 Рыбинск Ж 30.0 0 NaN 1 1 1 107683.34 0
3 225363 866.0 Ярославль Ж 51.0 5 1524746.26 2 0 1 174423.53 1
4 157978 730.0 Ярославль М 34.0 5 174.00 1 1 0 67353.16 1
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000 entries, 0 to 9999
Data columns (total 12 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   USERID         10000 non-null  int64  
 1   score          10000 non-null  float64
 2   city           10000 non-null  object 
 3   gender         10000 non-null  object 
 4   age            9974 non-null   float64
 5   equity         10000 non-null  int64  
 6   balance        7705 non-null   float64
 7   products       10000 non-null  int64  
 8   credit_card    10000 non-null  int64  
 9   last_activity  10000 non-null  int64  
 10  EST_SALARY     10000 non-null  float64
 11  churn          10000 non-null  int64  
dtypes: float64(4), int64(6), object(2)
memory usage: 937.6+ KB
None
USERID              0
score               0
city                0
gender              0
age                26
equity              0
balance          2295
products            0
credit_card         0
last_activity       0
EST_SALARY          0
churn               0
dtype: int64
Количество явных дубликатов в датасете data: 0

Проверим на неявные дубликаты, для этого выведем уникальные значения каждого столбца:

In [4]:
def checking_implicit_duplicates(df, column):
    print(f"Уникальные значения стобца {column}:")
    print(df[column].unique())
    
for column in bank_scrooge.columns:
    checking_implicit_duplicates(bank_scrooge, column)
    print()
Уникальные значения стобца USERID:
[183012 146556 120722 ... 115639 148700  96267]

Уникальные значения стобца score:
[ 850.  861.  892.  866.  730.  856.  807.  825.  923.  906.  927.  921.
  845.  837.  815.  731.  829.  793.  876.  922.  772.  896.  871.  732.
  898.  806.  766.  776.  868.  946.  828.  786.  944.  863.  822.  794.
  924.  811.  814.  956.  935.  826.  849.  901.  893.  792.  888.  831.
  748.  820.  860.  914.  930.  948.  789.  737.  812.  933.  809.  749.
  873.  859.  950.  937.  835.  768.  767.  785.  846.  750.  816.  894.
  891.  788.  758.  787.  821.  855.  779.  791.  838.  971.  797.  841.
  915.  985.  870.  897.  763.  774.  686. 1000.  875.  819.  869.  854.
  810.  761.  802.  958.  917.  745.  736.  908.  928.  884.  853.  780.
  726.  881.  975.  852.  965.  899.  890.  836.  818.  880.  795.  916.
  775.  902.  918.  842.  771.  895.  801.  721.  966.  804.  885.  986.
  753.  832.  979.  798.  754.  756.  823.  658.  734.  954.  827.  929.
  760.  688.  659.  817.  718.  705.  920.  652.  649.  862.  770.  824.
  934.  952.  977.  840.  709.  878.  808.  900.  778.  800.  813.  905.
  925.  697.  942.  848.  717.  712.  973.  879.  987.  805.  872.  904.
  939.  993.  790.  799.  943.  903.  839.  882.  765.  883.  964.  833.
  739.  911.  926.  751.  803.  762.  907.  910.  694.  867.  877.  706.
  708.  691.  742.  889.  919.  864.  663.  913.  940.  887.  953.  746.
  699.  858.  834.  847.  727.  782.  720.  938.  711.  725.  912.  851.
  743.  685.  830.  701.  733.  777.  784.  949.  724.  769.  687.  773.
  857.  963.  741.  931.  974.  764.  886.  844.  874.  909.  951.  947.
  689.  715.  865.  945.  796.  682.  714.  976.  992.  707.  713.  781.
  668.  843.  932.  752.  962.  759.  757.  967.  646.  738.  722.  936.
  664.  661.  728.  997.  755.  673.  703.  941.  988.  695.  961.  960.
  651.  696.  653.  983.  972.  716.  692.  959.  783.  735.  968.  702.
  740.  747.  955.  970.  990.  681.  744.  729.  982.  969.  980.  978.
  710.  719.  655.  650.  991.  642.  700.  981.  657.  996.  669.  999.
  698.  989.  957.  666.  671.  678.  693.  648.  690.  684.  994.  984.
  723.  667.  654.  680.  998.  660.  670.  656.  665.  662.  704.  995.]

Уникальные значения стобца city:
['Рыбинск' 'Ярославль' 'Ростов']

Уникальные значения стобца gender:
['Ж' 'М']

Уникальные значения стобца age:
[25. 37. 30. 51. 34. 56. 39. 38. 54. 67. 52. 41. 31. 28. 35. 42. 45. 53.
 40. 43. 47. 48. 61. 62. 36. 26. 27. 60. 72. 50. 46. 55. 58. 71. 44. 49.
 33. 32. 66. 29. 68. 73. 69. 59. 21. 65. 57. 63. 80. 24. 70. 77. 64. 23.
 20. 18. 19. 78. 74. 76. 22. 75. nan 83. 82. 81. 84. 79. 86.]

Уникальные значения стобца equity:
[1 5 0 4 3 2 6 7 8 9]

Уникальные значения стобца balance:
[  59214.82  850594.33        nan ...   78144.65  865457.97 1180886.7 ]

Уникальные значения стобца products:
[2 3 1 4 5 0]

Уникальные значения стобца credit_card:
[0 1]

Уникальные значения стобца last_activity:
[1 0]

Уникальные значения стобца EST_SALARY:
[ 75719.14  86621.77 107683.34 ... 108905.09  86874.9   21422.37]

Уникальные значения стобца churn:
[1 0]

Построим распределение данных по данным столбцов:

In [5]:
bank_scrooge.hist(figsize = (40, 30));

Вывод: походу выполнения раздела "Обзор данных" были импортированы необходимые библиотеки для работы и визуализации данных. Был загружен датасет в переменную bank_scrooge. Также были выведены первые 5 строк датасета на экран, для ознакомления с данными. Были построены гистограммы для данных столбцов датасета, чтобы ознакомиться с распределениями данных. Аномалий в данных не было обнаружено. Были обнаружены пропуски в данных, а именно в столбцах: age и balance, их количество равно 26 и 2295 соответственно. Также необходимо привести все названия столбцов к "змеиному виду", т.е. заглавные буквы заменить на строчные, а также разделить слова нижним знаком подчеркивания. Возможно, стоит заменить тип данных столбца score с типа данных float на int, так как на первый взгляд там только целые числа. Столбец данных age (возраст) необходимо перевести в тип данных int, так как возраст не может быть дробный. Для удобства можно также перевести столбцы balance (баланс на счете) и estimated_salary (заработная плата клиента) в тип данных int, так нам не смысла знать сумму до копеек, а это округление не сыграет на точности. Явные и неявные дубликаты в данных не были обнаружены. Также есть смысл заменить численные значения (0 или 1) в столбцах last_activity (активный клиент), credit_card (есть ли кредитная карта), churn (ушёл или нет) на категориальные значения, например, на слова да или нет.

Предобработка данных:¶

Приведение столбцов к нижнему регистру и добавление нижнего знака подчеркивания между словами:

In [6]:
bank_scrooge.columns = bank_scrooge.columns.str.lower()
bank_scrooge =  bank_scrooge.rename(columns = {'userid': 'user_id', 'creditcard': 'credit_card', 'est_salary': 'estimated_salary'})

Работа с пропусками в данных:

Посмотрим на столбец age в котором 26 пропусков:

In [7]:
bank_scrooge['age'].value_counts()
Out[7]:
36.0    414
37.0    402
33.0    392
35.0    388
34.0    384
       ... 
83.0      3
82.0      2
84.0      2
79.0      2
86.0      2
Name: age, Length: 68, dtype: int64

Вычислим отношение пропущенных значений в столбце ко всему количеству данных:

In [8]:
print(f"Доля пропущенных значений ко всему количеству равно: {bank_scrooge['age'].isna().sum() / 10000}")
Доля пропущенных значений ко всему количеству равно: 0.0026

Посмотрим на строки, где есть пропущенные значения в столбце age:

In [9]:
bank_scrooge[bank_scrooge['age'].isnull()]
Out[9]:
user_id score city gender age equity balance products credit_card last_activity estimated_salary churn
1247 228075 932.0 Ярославль М NaN 5 7601719.20 2 1 1 408121.16 0
2165 187635 692.0 Рыбинск Ж NaN 0 NaN 1 1 1 160368.82 0
2444 221156 913.0 Ярославль М NaN 0 NaN 1 1 1 135693.24 0
3091 138660 836.0 Ростов Ж NaN 5 294315.53 2 0 1 63310.22 1
4912 210674 834.0 Рыбинск М NaN 1 238330.52 2 0 1 93775.06 0
5470 218868 827.0 Рыбинск Ж NaN 4 448959.07 2 1 1 67835.95 0
5495 151662 884.0 Рыбинск Ж NaN 0 NaN 1 1 1 137500.77 0
7236 210135 908.0 Рыбинск Ж NaN 4 1120340.31 3 1 1 85002.15 0
7248 219343 920.0 Рыбинск Ж NaN 0 NaN 1 1 0 159248.67 0
7345 184913 829.0 Ярославль Ж NaN 3 188648.77 2 0 1 75206.90 0
7409 214031 777.0 Ярославль М NaN 2 171510.23 1 1 1 75409.63 0
8015 198635 670.0 Ярославль Ж NaN 0 NaN 1 1 1 168699.33 0
8070 226550 940.0 Рыбинск М NaN 0 NaN 1 0 1 147696.95 0
8293 216848 930.0 Ярославль М NaN 0 NaN 1 1 1 199542.51 0
8385 206759 915.0 Рыбинск М NaN 0 NaN 1 1 0 71179.53 0
8449 210898 805.0 Ярославль Ж NaN 0 NaN 1 0 1 922080.25 0
8632 221197 893.0 Ярославль М NaN 0 NaN 1 1 0 173929.92 0
8785 127440 663.0 Ярославль М NaN 0 NaN 1 1 1 117197.56 0
9104 222480 776.0 Рыбинск Ж NaN 5 796735.09 1 1 1 55073.63 0
9301 202983 942.0 Рыбинск Ж NaN 0 NaN 1 1 1 163804.73 0
9380 187459 894.0 Рыбинск М NaN 0 NaN 1 1 0 178012.28 0
9457 141945 929.0 Ярославль М NaN 0 NaN 1 1 0 381868.89 0
9632 185829 927.0 Ярославль М NaN 0 NaN 1 1 0 231254.86 0
9634 221809 917.0 Ярославль М NaN 0 NaN 1 1 1 192644.15 0
9667 163657 849.0 Ярославль М NaN 4 1254013.85 2 1 1 119106.67 0
9819 140934 832.0 Рыбинск Ж NaN 3 385763.16 2 0 1 59651.35 0

Как можно увидеть, что пропусков в столбце age не так много, всего 26 или 0.26% от всего количества. А также большинство строк, в которых есть пропуск по возрасту, также имеется пропуски и в столбце balance (баланс на счёте). Но также есть среди полученных данных и довольно богатые клиенты, например, под номером user_id 228075, у которого более 7 млн. рублей на балансе. И нет возможности заменить пропуски каким-либо релевантным значением, например, медианным значением по категориям. Так что в данном случае, лучше пропуски удалить, для того чтобы в будущем работать с данным столбцом:

In [10]:
bank_scrooge = bank_scrooge.dropna(subset = ['age'])

Посмотрим на столбец balance в котором было 2295 пропусков:

In [11]:
bank_scrooge['balance']
Out[11]:
0         59214.82
1        850594.33
2              NaN
3       1524746.26
4           174.00
           ...    
9995      78144.65
9996           NaN
9997           NaN
9998     865457.97
9999    1180886.70
Name: balance, Length: 9974, dtype: float64

Вычислим отношение пропущенных значений в столбце ко всему количеству данных:

In [12]:
print(f"Доля пропущенных значений ко всему количеству равно: {bank_scrooge['balance'].isna().sum() / 10000}")
Доля пропущенных значений ко всему количеству равно: 0.2279

Доля пропущенных значений в столбце balance довольно высока, она равна 0.2279 или 23%. Так что пропущенные значения точно не стоит удалять, лучше попробовать выяснить природу этих пропусков. Для этого посмотрим на строки, где есть пропущенные значения в столбце balance:

In [13]:
bank_scrooge[bank_scrooge['balance'].isnull()]
Out[13]:
user_id score city gender age equity balance products credit_card last_activity estimated_salary churn
2 120722 892.0 Рыбинск Ж 30.0 0 NaN 1 1 1 107683.34 0
9 133130 906.0 Ярославль Ж 67.0 0 NaN 1 0 1 238055.53 0
10 148929 927.0 Ростов М 52.0 0 NaN 1 1 1 196820.07 0
11 172184 921.0 Ростов М 41.0 0 NaN 1 1 1 217469.48 0
19 127034 922.0 Рыбинск Ж 53.0 0 NaN 1 0 0 147094.82 0
... ... ... ... ... ... ... ... ... ... ... ... ...
9976 208085 876.0 Ростов М 38.0 0 NaN 1 0 0 171763.69 0
9984 125941 729.0 Ярославль Ж 42.0 0 NaN 1 1 1 687538.70 0
9993 219924 884.0 Рыбинск Ж 36.0 0 NaN 1 1 1 169844.88 0
9996 139170 894.0 Ярославль М 46.0 0 NaN 1 1 0 196898.29 0
9997 115639 903.0 Ярославль М 24.0 0 NaN 2 1 1 108905.09 0

2279 rows × 12 columns

Исходя из полученных результатов, очень сложно сделать вывод, о том какая причина стоит в пропусках данных, может быть это дебетовый счет в банке. Или же ошибка на сервере, но это маловероятно, так как такое большое количество пропусков только в данном столбце. В данном случае лучше всего эти пропуски оставить как есть.

Изменение типов данных:

Посмотрим на столбец score и выясним можно ли его привести у типу данных int:

In [14]:
bank_scrooge['score']
Out[14]:
0       850.0
1       861.0
2       892.0
3       866.0
4       730.0
        ...  
9995    814.0
9996    894.0
9997    903.0
9998    777.0
9999    828.0
Name: score, Length: 9974, dtype: float64
In [15]:
bank_scrooge['score'].value_counts()
Out[15]:
900.0    80
889.0    75
899.0    70
894.0    69
912.0    68
         ..
654.0     1
671.0     1
673.0     1
684.0     1
995.0     1
Name: score, Length: 348, dtype: int64

Переведем столбец score в тип данных int:

In [16]:
bank_scrooge['score'] = bank_scrooge['score'].astype('int')

Переведем столбцы age, balance и estimated_salary в тип данных int:

In [17]:
bank_scrooge['estimated_salary'] = bank_scrooge['estimated_salary'].astype('int')

Замена данных:

In [18]:
bank_scrooge['credit_card'].value_counts()
Out[18]:
1    6784
0    3190
Name: credit_card, dtype: int64

Приведение столбца gender к числовому виду, чтобы в дальнейшем было удобно работать с этими столбцом:

In [19]:
bank_scrooge['gender'] = bank_scrooge['gender'].str.replace("М", '1')
bank_scrooge['gender'] = bank_scrooge['gender'].str.replace("Ж", '0')
bank_scrooge['gender'] = bank_scrooge['gender'].astype('int')

В итоге получается в столбце gender 1 будет значить мужской пол, а 0 - женский пол.

Исследовательский анализ данных:¶

Получим описательную статистику для данных:

In [20]:
bank_scrooge.drop(columns = ['user_id', 'city', 'gender'], axis = 1).describe()
Out[20]:
score age equity balance products credit_card last_activity estimated_salary churn
count 9974.000000 9974.000000 9974.000000 7.695000e+03 9974.000000 9974.000000 9974.000000 9.974000e+03 9974.000000
mean 848.682575 42.734409 2.630840 8.272456e+05 1.871466 0.680168 0.522859 1.477863e+05 0.182374
std 65.399720 12.179971 1.979965 1.980327e+06 0.792729 0.466435 0.499502 1.392855e+05 0.386172
min 642.000000 18.000000 0.000000 0.000000e+00 0.000000 0.000000 0.000000 2.546000e+03 0.000000
25% 802.000000 33.000000 0.000000 2.956987e+05 1.000000 0.000000 0.000000 7.525125e+04 0.000000
50% 853.000000 40.000000 3.000000 5.242953e+05 2.000000 1.000000 1.000000 1.196265e+05 0.000000
75% 900.000000 51.000000 4.000000 9.800512e+05 2.000000 1.000000 1.000000 1.744990e+05 0.000000
max 1000.000000 86.000000 9.000000 1.191136e+08 5.000000 1.000000 1.000000 1.395064e+06 1.000000

Проведем корриляционный анализ:

In [21]:
bank_scrooge_corr = bank_scrooge.drop(columns = ['user_id'], axis = 1).phik_matrix(verbose = False)
display(bank_scrooge_corr)
score city gender age equity balance products credit_card last_activity estimated_salary churn
score 1.000000 0.090925 0.070446 0.036178 0.527334 0.170887 0.307399 0.208318 0.053418 0.395214 0.228080
city 0.090925 1.000000 0.011924 0.093705 0.062559 0.039931 0.223196 0.074660 0.030399 0.170948 0.016431
gender 0.070446 0.011924 1.000000 0.286026 0.063699 0.045434 0.122023 0.195831 0.012733 0.144090 0.219731
age 0.036178 0.093705 0.286026 1.000000 0.043708 0.024989 0.133418 0.157196 0.089172 0.358167 0.182538
equity 0.527334 0.062559 0.063699 0.043708 1.000000 0.000000 0.441413 0.221467 0.051218 0.285543 0.352103
balance 0.170887 0.039931 0.045434 0.024989 0.000000 1.000000 0.000000 0.000000 0.000000 0.374366 0.024672
products 0.307399 0.223196 0.122023 0.133418 0.441413 0.000000 1.000000 0.459117 0.191921 0.157477 0.440923
credit_card 0.208318 0.074660 0.195831 0.157196 0.221467 0.000000 0.459117 1.000000 0.049306 0.038496 0.202374
last_activity 0.053418 0.030399 0.012733 0.089172 0.051218 0.000000 0.191921 0.049306 1.000000 0.044890 0.262164
estimated_salary 0.395214 0.170948 0.144090 0.358167 0.285543 0.374366 0.157477 0.038496 0.044890 1.000000 0.048803
churn 0.228080 0.016431 0.219731 0.182538 0.352103 0.024672 0.440923 0.202374 0.262164 0.048803 1.000000

Можно увидеть, что есть прямая зависимость между столбцом churn и столбцами: equity (количество баллов собственности), last_activity (активный клиент), score (баллы кредитного скоринга), gender (пол) и age (возраст). А самая большая прямая зависимость со столбцом products (количество продуктов, которыми пользуется клиент).

Построим столбец корриляций:

In [22]:
sns.heatmap(
            bank_scrooge_corr.loc[:, ['churn']].drop('churn').sort_values('churn'),
            annot = True, fmt = '.0%', cmap = 'flag', center = 0);

По уменьшению корриляции для столбца churn идут столбцы: products, equity, last_activity, score, gender, credit_card, age, estimated_salary, balance, city.

Построим матрицу корреляций:

Построим тепловую карту, вычесленных корриляций (корреляции Пирсена):

In [23]:
sns.heatmap(bank_scrooge_corr, annot = True, square = True)
plt.title('Матрица корреляций', fontsize = 30)
sns.set(rc = {'figure.figsize': (15, 15)})
plt.yticks(fontsize = 15)
plt.xticks(fontsize = 15)
plt.show()

Вывод: наибольшая корреляция между churn (ушел клиент или нет) и столбцом products (количество продуктов, которыми пользуется клиент). Т.е. чем большими продуктами пользуется клиент, тем менее вероятно, что он уйдет. Также присутствует корреляция для столбца equity (количество баллов собственности). А также для столбца last_activity (активный ли клиент).

Категоризируем признаки с помощью нормированных гистограмм а также определим категории клиентов, которые склонны к оттоку:¶

По возрасту age:

In [24]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'age',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Исходя из построенной нормированной гистограммы, получаются категории:

  1. От 18 до 25 (young);
  2. от 25 до 35 (mature);
  3. от 35 до 50 (middle age);
  4. от 50 до 60 (old);
  5. от 60 (pensioner).

Наибольший отток клиентов происходят в возрасте от 25 до 35, а также от 50 до 60 лет есть тоже значительный отток.

Создадим новый столбец age_category, где в зависимости от возраста клиента, укажем его принадлежность к определенному возрасту:

In [25]:
bank_scrooge.loc[(bank_scrooge['age'] < 25), 'age_category'] = 'young'
bank_scrooge.loc[(bank_scrooge['age'] >= 25) & (bank_scrooge['age'] <= 35), 'age_category'] = 'mature'
bank_scrooge.loc[(bank_scrooge['age'] > 35) & (bank_scrooge['age'] < 50), 'age_category'] = 'middle age'
bank_scrooge.loc[(bank_scrooge['age'] >= 50) & (bank_scrooge['age'] <= 60), 'age_category'] = 'old'
bank_scrooge.loc[(bank_scrooge['age'] > 60), 'age_category'] = 'pensioner'

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов по созданным категориям возраста:

In [26]:
table_age = bank_scrooge.pivot_table(index = 'churn', columns = 'age_category', values = 'age', aggfunc = 'count')
display(table_age)

labels = ['оставшиеся', 'ушедшие']
age_category mature middle age old pensioner young
churn
0 2390 3294 1300 966 205
1 665 588 468 67 31

Построим круговую диаграмму для того чтобы определить какая возрастная категория более склонна к оттоку:

In [27]:
table_age = table_age.reset_index()

fig = make_subplots(rows = 1, cols = 5, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_age[table_age.columns[1]],
     labels = labels,
     title = 'mature(25-35)'), 
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_age[table_age.columns[2]],
     labels = labels,
     title = 'middle age(35-50)'),
     row = 1, col = 2)

fig.add_trace(go.Pie(
     values = table_age[table_age.columns[3]],
     labels = labels,
     title = 'old(50-60)'),
     row = 1, col = 3)

fig.add_trace(go.Pie(
     values = table_age[table_age.columns[4]],
     labels = labels,
     title = 'pensioner(60+)'),
     row = 1, col = 4)

fig.add_trace(go.Pie(
     values = table_age[table_age.columns[5]],
     labels = labels,
     title = 'young(18-25)'),
     row = 1, col = 5)

fig.show()

Вывод: клиенты в возрасте от 50 до 60 лет включительно, более всего склонны к оттоку, относительно других групп, целых 26.5% от всего количества. Далее идет группа возраста от 25 до 35 лет включительно, в ней отток равен 21.8%. Далее идет возрастная категория от 35 до 50, у нее процент оттока равен 15.1. Далее группа молодых от 18 до 25 лет, с оттоком 13.1%. И наименьший процент оттока у категории пенсионеров 60+ у нее отток равен всего 6.49 процента.

По полу gender:

In [28]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'gender',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от их пола:

In [29]:
table_gender = bank_scrooge.pivot_table(index = 'churn', columns = 'gender', values = 'age', aggfunc = 'count')
display(table_gender)
gender 0 1
churn
0 4347 3808
1 636 1183

Построим круговую диаграмму для того чтобы определить какая пол более склонен к оттоку:

In [30]:
table_gender = table_gender.reset_index()

fig = make_subplots(rows = 1, cols = 2, specs = [[{"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_gender[table_gender.columns[1]],
     labels = labels,
     title = 'Женщины'), 
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_gender[table_gender.columns[2]],
     labels = labels,
     title = 'Мужчины'),
     row = 1, col = 2)

fig.show()

Вывод: можно заметить, что клиенты мужского пола более склонны к оттоку, нежели женщины. У мужчин 23.7% оттока от всего количества, когда у женщин всего 12.8%.

По количеству баллов собственности equity:

In [31]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'equity',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от количества их баллов собственности:

In [32]:
table_equity = bank_scrooge.pivot_table(index = 'churn', columns = 'equity', values = 'age', aggfunc = 'count')
display(table_equity)
equity 0 1 2 3 4 5 6 7 8 9
churn
0 2486 680 885 1219 1383 1339 103 43 11 6
1 90 93 166 322 464 576 58 37 6 7

Построим круговую диаграмму для того чтобы определить как влияет количества баллов собственности клиента на их отток (построим наглядный график для клиентов, у которых баллов не более 5, так как большее количество баллов у очень маленького количества клиентов):

In [33]:
fig = make_subplots(rows = 1, cols = 6, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_equity[table_equity.columns[1]],
     labels = labels,
     title = '0 баллов'), 
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_equity[table_equity.columns[2]],
     labels = labels,
     title = '1 балл'),
     row = 1, col = 2)

fig.add_trace(go.Pie(
     values = table_equity[table_equity.columns[3]],
     labels = labels,
     title = '2 балла'),
     row = 1, col = 3)

fig.add_trace(go.Pie(
     values = table_equity[table_equity.columns[4]],
     labels = labels,
     title = '3 балла'),
     row = 1, col = 4)

fig.add_trace(go.Pie(
     values = table_equity[table_equity.columns[5]],
     labels = labels,
     title = '4 балла'),
     row = 1, col = 5)

fig.add_trace(go.Pie(
     values = table_equity[table_equity.columns[6]],
     labels = labels,
     title = '5 баллов'),
     row = 1, col = 6)

fig.show()

Вывод: чем больше у клиентов баллов собственности, тем более вероятен отток клиента. Например, процент оттока клиентов, у кого 0 баллов равен 3.49% от всего количества. А вот у клиентов, у которых 5 баллов уже 30.1% оттока. И с каждым следующим баллов сохраняется тенденция увеличения доли оттока клиентов.

По количеству продуктов, которыми пользуется клиент products:

In [34]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'products',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от количества продуктов, которыми пользуется клиент:

In [35]:
table_products = bank_scrooge.pivot_table(index = 'churn', columns = 'products', values = 'age', aggfunc = 'count')
display(table_products)
products 0 1 2 3 4 5
churn
0 NaN 3088.0 4141.0 741.0 174.0 11.0
1 1.0 235.0 978.0 297.0 300.0 8.0

Построим круговую диаграмму для того чтобы определить как влияет количеству продуктов, которыми пользуется клиент на их отток (круговую диаграмму для клиентов, у которых 0 или 5 продуктов, можно не рассматривать, так как таких клиентов крайне мало):

In [36]:
table_products = table_products.reset_index()

fig = make_subplots(rows = 1, cols = 4, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_products[table_products.columns[2]],
     labels = labels,
     title = '1 продукт'),
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_products[table_products.columns[3]],
     labels = labels,
     title = '2 продукта'),
     row = 1, col = 2)

fig.add_trace(go.Pie(
     values = table_products[table_products.columns[4]],
     labels = labels,
     title = '3 продукта'),
     row = 1, col = 3)

fig.add_trace(go.Pie(
     values = table_products[table_products.columns[5]],
     labels = labels,
     title = '4 продукта'),
     row = 1, col = 4)

fig.show()

Вывод: заметна тенденция, что чем больше продуктов, которыми пользуется клиент, тем больше доля оттока таких клиентов. Например, клиент, у которого один продукт процент оттока таких клиентов равен 7, от всего количества. А вот для клиентов, у которых 4 продукта процент оттока уже равен 63.3%. Что довольно логично.

Есть ли кредитная карта credit_card:

In [37]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'credit_card',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от наличия кредитной карты:

In [38]:
table_credit_card = bank_scrooge.pivot_table(index = 'churn', columns = 'credit_card', values = 'age', aggfunc = 'count')
display(table_credit_card)
credit_card 0 1
churn
0 2374 5781
1 816 1003

Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от наличия кредитной карты:

In [39]:
table_credit_card = table_credit_card.reset_index()

fig = make_subplots(rows = 1, cols = 2, specs = [[{"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_credit_card[table_credit_card.columns[1]],
     labels = labels,
     title = 'Нет кредитной карты'),
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_credit_card[table_credit_card.columns[2]],
     labels = labels,
     title = 'Есть кредитная карта'),
     row = 1, col = 2)

fig.show()

Вывод: больше отток у клиентов, у которых нет кредитной карты, он равен почти 26% от всего количества, для клиентов у которых есть кредитная карта, он равен всего 15%.

Рекомендация: необходимо проводить акции для клиентов, например, предлагать кредитные карты с бесплатным обслуживанием в течение года, при оформлении. Так как статистика показывает, что для клиентов, у которых оформлена кредитная карта, им менее свойственен отток.

По активности клиента last_activity:

In [40]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'last_activity',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от активности клиента:

In [41]:
table_activity = bank_scrooge.pivot_table(index = 'churn', columns = 'last_activity', values = 'age', aggfunc = 'count')
display(table_activity)
last_activity 0 1
churn
0 4217 3938
1 542 1277

Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от активности клиента:

In [42]:
table_activity = table_activity.reset_index()

fig = make_subplots(rows = 1, cols = 2, specs = [[{"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_activity[table_activity.columns[1]],
     labels = labels,
     title = 'Активный клиент'),
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_activity[table_activity.columns[2]],
     labels = labels,
     title = 'Не активный клиент'),
     row = 1, col = 2)

fig.show()

Вывод: что и следовало ожидать, что у активных клиентов процент оттока меньше. У активных клиентов процент оттока равен 11.4% от всего количества. А вот у не активных клиентов он равен почти 25%.

Рекомендации: необходимо анализировать последнюю активность клиента, и если последняя активность была более, например, месяца назад. То необходимо стараться завлечь клиента пользованием картой. К примеру, некими акциями или кэшбэком, или же беспроцентными переводами в течение месяца.

По баллам кредитного скоринга score:

In [43]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'score',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Наибольший отток клиентов происходит в диапазоне баллов кредитного скорринга от 825 до 910 (включительно), а также довольно ощутимый отток происходит от 930 до до 940 (включительно). Выделим основные группы:

  1. менее 825 баллов кредитного скоринга;
  2. от 825 до 910 баллов кредитного скоринга;
  3. от 910 до 930 баллов кредитного скоринга;
  4. от 930 до 940 баллов кредитного скоринга;
  5. свыше 940 баллов кредитного скоринга.

Создадим новый столбец score_category, где в зависимости от баллов кредитного скоринга, укажем принадлежность клиента к определенной категории:

In [44]:
bank_scrooge.loc[(bank_scrooge['score'] < 825), 'score_category'] = '-825'
bank_scrooge.loc[(bank_scrooge['score'] >= 825) & (bank_scrooge['score'] <= 910), 'score_category'] = '825-910'
bank_scrooge.loc[(bank_scrooge['score'] > 910) & (bank_scrooge['score'] < 930), 'score_category'] = '910-930'
bank_scrooge.loc[(bank_scrooge['score'] >= 930) & (bank_scrooge['score'] <= 940), 'score_category'] = '930-940'
bank_scrooge.loc[(bank_scrooge['score'] > 940), 'score_category'] = '940+'

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от баллов кредитного скоринга:

In [45]:
table_score = bank_scrooge.pivot_table(index = 'churn', columns = 'score_category', values = 'score', aggfunc = 'count')
display(table_score)
score_category -825 825-910 910-930 930-940 940+
churn
0 3199 3418 757 244 537
1 409 1095 165 65 85

Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от баллов кредитного скоринга:

In [46]:
table_score = table_score.reset_index()

fig = make_subplots(rows = 1, cols = 5, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_score[table_score.columns[1]],
     labels = labels,
     title = 'Менее 825 баллов кредитного скоринга'),
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_score[table_score.columns[2]],
     labels = labels,
     title = 'От 825 до 910 баллов кредитного скоринга'),
     row = 1, col = 2)

fig.add_trace(go.Pie(
     values = table_score[table_score.columns[3]],
     labels = labels,
     title = 'От 910 до 930 баллов кредитного скоринга'),
     row = 1, col = 3)

fig.add_trace(go.Pie(
     values = table_score[table_score.columns[4]],
     labels = labels,
     title = 'От 930 до 940 баллов кредитного скоринга'),
     row = 1, col = 4)

fig.add_trace(go.Pie(
     values = table_score[table_score.columns[5]],
     labels = labels,
     title = 'Свыше 940 баллов кредитного скоринга'),
     row = 1, col = 5)

fig.show()

Вывод: можно увидеть, что наибольший процентный отток клиентов, у которых среднее количество баллов кредитного скоринга (от 825 до 910) он равен 24.3% от всего количества. Далее идет категория, у которой много баллов кредитного скоринга (от 940 до 940) процент оттока клиентов данной категории равен почти 21%. И самый малый процент оттока клиентов, у которых мало баллов кредитного скоринга (до 825) их процент оттока равен 11.3.

По городу city:

In [47]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'city',
            stat = 'density',
            common_norm = False,
            palette = 'flag',
            bins = 3 
           );

Клиентам из городов: Ярославль и Ростов более свойственен отток, нежели клиенты, который проживают в Рыбинске.

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от города:

In [48]:
table_city = bank_scrooge.pivot_table(index = 'churn', columns = 'city', values = 'age', aggfunc = 'count')
display(table_city)
city Ростов Рыбинск Ярославль
churn
0 1151 2246 4758
1 265 437 1117

Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от города:

In [49]:
table_city = table_city.reset_index()

fig = make_subplots(rows = 1, cols = 3, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_city[table_city.columns[1]],
     labels = labels,
     title = 'Ростов'),
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_city[table_city.columns[2]],
     labels = labels,
     title = 'Рыбинск'),
     row = 1, col = 2)

fig.add_trace(go.Pie(
     values = table_city[table_city.columns[3]],
     labels = labels,
     title = 'Ярославль'),
     row = 1, col = 3)

fig.show()

Вывод: почти одинаковых процент оттока клиентов из города Ростов и Ярославль он равен 18.7 и 19%. А вот для клиентов из города Рыбинска он равен 16.3% от всего количества.

По балансу на счете balance:

In [50]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge.query('balance != -1'),
            hue = 'churn',
            x = 'balance',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Видим выбросы все, что выше 5000000, сделаем срез данных, чтобы получить наглядные результаты.

In [51]:
plt.figure(figsize = (15, 10))
plt.xlim([0, 5000000])
sns.histplot(
            data = bank_scrooge.query('balance != -1'),
            hue = 'churn',
            x = 'balance',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Исходя из построенной нормированной гистограммы, получаются три категории:

  1. До 800000 (low balance);
  2. от 800000 до 4200000 (middle balance);
  3. Свыше 4200000 (high balance).

И большой отток среди тех клиентов, у которых баланс свыше 1000000 рублей.

Создадим новый столбец balance_category, где в зависимости от баланса клиента на счёте, укажем его принадлежность к определенной категории (low_balance - низкий баланс, middle_balance - средний баланс, high_balance - высокий баланс):

In [52]:
bank_scrooge.loc[(bank_scrooge['balance'] <= 800000), 'balance_category'] = 'low_balance'
bank_scrooge.loc[(bank_scrooge['balance'] > 800000) & (bank_scrooge['balance'] < 4200000), 'balance_category'] = 'middle_balance'
bank_scrooge.loc[(bank_scrooge['balance'] >= 4200000), 'balance_category'] = 'high_balance'

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от баланса:

In [53]:
table_balance = bank_scrooge.pivot_table(index = 'churn', columns = 'balance_category', values = 'balance', aggfunc = 'count')
display(table_balance)
balance_category high_balance low_balance middle_balance
churn
0 70 4270 1549
1 36 918 852

Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в баланса:

In [54]:
table_balance = table_balance.reset_index()

fig = make_subplots(rows = 1, cols = 3, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_balance[table_balance.columns[1]],
     labels = labels,
     title = 'Большой баланс'),
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_balance[table_balance.columns[2]],
     labels = labels,
     title = 'Низкий баланс'),
     row = 1, col = 2)

fig.add_trace(go.Pie(
     values = table_balance[table_balance.columns[3]],
     labels = labels,
     title = 'Средний баланс'),
     row = 1, col = 3)

fig.show()

Вывод: наибольший отток клиентов, у которых баланс на счёте находится в диапазоне от 800000 до 4200000 рублей, для них процент оттока равен 35.5 от всего количества. Далее идут клиенты из категории большого баланса (свыше 4200000 рублей на счету) для них процент оттока равен 34. Ну и самый маленький процент оттока у клиентов с низким балансом, он равен 12.5.

По заработной плате estimated_salary:

In [55]:
plt.figure(figsize = (15, 10))
sns.histplot(
            data = bank_scrooge,
            hue = 'churn',
            x = 'estimated_salary',
            stat = 'density',
            common_norm = False,
            palette = 'flag'
           );

Исходя из построенной нормированной гистограммы, получаются три категории:

  1. До 80000 (low salary);
  2. от 80000 до 300000 (middle salary);
  3. свыше 300000 (high salary).

Отток клиентов с зарплатой ниже 80 тыс. рублей не большой, а вот для клиентов с более высокой зарплатой отток становится больше. А наибольший отток клиентов происходит в диапозоне от 80 тыс. до 300 тыс. рублей заработной платы.

Создадим новый столбец salary_category, где в зависимости от заработной платы клиента, укажем его принадлежность к определенной категории (low_salary - низкая заработная плата, middle_salary - средняя заработная плата, high_salary - высокая заработная плата):

In [56]:
bank_scrooge.loc[(bank_scrooge['estimated_salary'] < 80000), 'salary_category'] = 'low_salary'
bank_scrooge.loc[(bank_scrooge['estimated_salary'] >= 80000) & (bank_scrooge['estimated_salary'] <= 300000), 'salary_category'] = 'middle_salary'
bank_scrooge.loc[(bank_scrooge['estimated_salary'] > 300000), 'salary_category'] = 'high_salary'

Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от заработной платы:

In [57]:
table_salary = bank_scrooge.pivot_table(index = 'churn', columns = 'salary_category', values = 'estimated_salary', aggfunc = 'count')
display(table_salary)
salary_category high_salary low_salary middle_salary
churn
0 467 2344 5344
1 95 418 1306

Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от заработной платы клиента:

In [58]:
table_salary = table_salary.reset_index()

fig = make_subplots(rows = 1, cols = 3, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])

fig.add_trace(go.Pie(
     values = table_salary[table_salary.columns[1]],
     labels = labels,
     title = 'Высокая ЗП'),
     row = 1, col = 1)

fig.add_trace(go.Pie(
     values = table_salary[table_salary.columns[2]],
     labels = labels,
     title = 'Низкая ЗП'),
     row = 1, col = 2)

fig.add_trace(go.Pie(
     values = table_salary[table_salary.columns[3]],
     labels = labels,
     title = 'Средняя ЗП'),
     row = 1, col = 3)

fig.show()

Вывод: наибольший процент оттока у клиентов, принадлежащих к категории средней ЗП (80 тыс. до 300 тыс. рублей) он равен почти 20 от всего количества. Далее идет категория клиентов с высокой ЗП (свыше 300 тыс. рублей) для нее процент оттока равен 16.9. Ну и самый маленький процент оттока клиентов с низкой ЗП (до 80 тыс. рублей) он равен почти 15.

Проверка статистических гипотез:¶

Проверка гипотезы различия дохода между теми клиентами, которые ушли и теми, которые остались:

Сформулируем гипотезы:

Нулевая гипотеза будет гласить, что различия в зарплате между теми клиентами, которые ушли и теми, которые остались, не значительные.

Альтернативная гипотеза будет гласить, что существуют различия в зарплате между теми клиентами, которые ушли и теми, которые остались.

p-value (уровень значимости) возьмем равный 0.05.

In [59]:
results = st.ttest_ind(
    bank_scrooge[bank_scrooge['churn'] == 0]['estimated_salary'],
    bank_scrooge[bank_scrooge['churn'] == 1]['estimated_salary'],
    equal_var = False)

alpha = 0.05 # значение уровня значимости

print('p-значение:', results.pvalue)

if results.pvalue < alpha:
    print(results.pvalue, "Отвергаем нулевую гипотезу")
else:
    print(results.pvalue, "Не получилось отвергнуть нулевую гипотезу") 
p-значение: 0.8515828926028186
0.8515828926028186 Не получилось отвергнуть нулевую гипотезу
In [60]:
# определим статистическую значимость между доходами в двух группах:
print('{0:.4f}'.format(stats.mannwhitneyu(bank_scrooge[bank_scrooge['churn'] == 0]['estimated_salary'], 
                                          bank_scrooge[bank_scrooge['churn'] == 1]['estimated_salary'])[1]))

# определим отншение среднего дохода клиентов, которые ушли, к среднему доходу клиентов, которые остались:
print('{0:.4f}'.format(bank_scrooge[bank_scrooge['churn'] == 0]['estimated_salary'].mean() / bank_scrooge[bank_scrooge['churn'] == 1]['estimated_salary'].mean()))
0.0002
0.9959

Вывод по полученным результатам:

p-value значительно больше 0.05. И равно 0.85, это значит, что не существует значимых статистических различий между зарплатами клиентов, которые ушли и теми, которые остались, поэтому принимаем нулевую гипотезу, и отвергаем альтернативную

Получается, что ЗП, у клиентов, которые остались, в среднем в 1.003 раза меньше тех, кто ушел.

Причина массового оттока клиентов заключается не в низком уровне зарплаты

Посчитаем статистическую значимость различий в значении кредитного скоринга между теми клиентами, которые ушли и теми, которые остались:

Сформулируем гипотезы:

Нулевая гипотеза будет гласить, что различие в значениях кредитного скоринга между теми клиентами, которые ушли и теми, которые остались, не значительное.

Альтернативная гипотеза будет гласить, что существует различие в значениях кредитного скоринга клиентов, которые ушли и теми, которые остались.

p-value (уровень значимости) возьмем равный 0.05.

In [61]:
results = st.ttest_ind(
    bank_scrooge[bank_scrooge['churn'] == 0]['score'],
    bank_scrooge[bank_scrooge['churn'] == 1]['score'],
    equal_var = False)

alpha = 0.05 # значение уровня значимости

print('p-значение:', results.pvalue)

if results.pvalue < alpha:
    print(results.pvalue, "Отвергаем нулевую гипотезу")
else:
    print(results.pvalue, "Не получилось отвергнуть нулевую гипотезу") 
p-значение: 8.60332714629934e-38
8.60332714629934e-38 Отвергаем нулевую гипотезу
In [62]:
print('{0:.4f}'.format(stats.mannwhitneyu(bank_scrooge[bank_scrooge['churn'] == 0]['score'], 
                                          bank_scrooge[bank_scrooge['churn'] == 1]['score'])[1]))

print('{0:.4f}'.format(bank_scrooge[bank_scrooge['churn'] == 0]['score'].mean() / bank_scrooge[bank_scrooge['churn'] == 1]['score'].mean()))
0.0000
0.9791

Вывод по полученным результатам:

p-value меньше 0.05. Значит существуют весомые статистические различия между значениями кредитного скоринга клиентов, которые ушли и теми, которые остались, поэтому принимаем альтернативную гипотезу, и отвергаем нулевую гипотезу.

Выходит, что кредитный скоринг клиентов, которые остались, в среднем в 1.02 раза выше тех, кто ушел, что как показали результаты является значимым расхождением. Причина массового оттока клиентов заключается в менее выгодных условиях выплате кредитов. Скорее всего, что клиенты, которые ушли из банка решили рефинансировать свой кредит в другом банке. Поэтому необходимо смягчить условия кредитования, а также, возможно, предоставлять кредитные каникулы некоторым клиентам, подходящие по портреты, описанные ранее.

Выделение высокооточных сегментов:

Сначала посмотрим на процент оттока по всем клиентам:

In [63]:
print(f"Процент оттока по всем клиентам банка равен: {round(((len(bank_scrooge.query('churn == 1')) / len(bank_scrooge)) * 100), 1)}")
Процент оттока по всем клиентам банка равен: 18.2

Выделим первый сигмент:

In [64]:
segment_1 = bank_scrooge.query("gender == 1 and city == 'Ярославль' and last_activity == 1")
print(f"Процент оттока клиентов для первого сегмента равен: {round(((len(segment_1.query('churn == 1')) / len(segment_1)) * 100), 1)}")
print(f"Всего клиентов в данном сегменте: {len(segment_1)}")
print(f"Всего клиентов которые ушли {len(segment_1.query('churn == 1'))}")
Процент оттока клиентов для первого сегмента равен: 29.9
Всего клиентов в данном сегменте: 1490
Всего клиентов которые ушли 445

Выделим второй сигмент:

In [65]:
segment_2 = bank_scrooge.query("age_category == 'old' and balance_category == 'middle_balance'")
print(f"Процент оттока клиентов для второго сегмента равен: {round(((len(segment_2.query('churn == 1')) / len(segment_2)) * 100), 1)}")
print(f"Всего клиентов в данном сегменте: {len(segment_2)}")
print(f"Всего клиентов которые ушли {len(segment_2.query('churn == 1'))}")
Процент оттока клиентов для второго сегмента равен: 45.5
Всего клиентов в данном сегменте: 550
Всего клиентов которые ушли 250

Выделим третий сигмент:

In [66]:
segment_3 = bank_scrooge.query("products >= 2 and credit_card == 0 and equity >= 5")
print(f"Процент оттока клиентов для второго сегмента равен: {round(((len(segment_3.query('churn == 1')) / len(segment_3)) * 100), 1)}")
print(f"Всего клиентов в данном сегменте: {len(segment_3)}")
print(f"Всего клиентов которые ушли {len(segment_3.query('churn == 1'))}")
Процент оттока клиентов для второго сегмента равен: 39.3
Всего клиентов в данном сегменте: 880
Всего клиентов которые ушли 346

Итоговый вывод:¶

Параметры, влияющие на отток клиентов из банка:¶

  • Возраст влияет на отток клиентов, к оттоку предрасположены клиенты молодого и среднего возраста, а вот люди свыше 60 лет намного реже подвержены к уходу из банка. Клиенты в возрасте от 50 до 60 лет включительно, более всего склонны к оттоку, относительно других групп, целых 26.5% от всего количества. Далее идет группа возраста от 25 до 35 лет включительно, в ней отток равен 21.8%. Далее идет возрастная категория от 35 до 50, у нее процент оттока равен 15.1. Далее группа молодых от 18 до 25 лет, с оттоком 13.1%. И наименьший процент оттока у категории пенсионеров 60+ у нее отток равен всего 6.49 процента.
  • клиенты мужского пола более склонны к оттоку, нежели женщины;
  • чем больше у клиентов баллов собственности, тем более вероятен отток клиента;
  • чем больше продуктов, которыми пользуется клиент, тем больше доля оттока таких клиентов;
  • больше отток у клиентов, у которых нет кредитной карты;
  • у активных клиентов процент оттока меньше;
  • можно увидеть, что наибольший процентный отток клиентов, у которых среднее количество баллов кредитного скоринга (от 825 до 910) он равен 24.3% от всего количества. Далее идет категория, у которой много баллов кредитного скоринга (от 940 до 940) процент оттока клиентов данной категории равен почти 21%. И самый малый процент оттока клиентов, у которых мало баллов кредитного скоринга (до 825) их процент оттока равен 11.3;
  • почти одинаковых процент оттока клиентов из города Ростов и Ярославль он равен 18.7 и 19%. А вот для клиентов из города Рыбинска он равен 16.3% от всего количества;
  • наибольший отток клиентов, у которых баланс на счёте находится в диапазоне от 800000 до 4200000 рублей, для них процент оттока равен 35.5 от всего количества. Далее идут клиенты из категории большого баланса (свыше 4200000 рублей на счету) для них процент оттока равен 34. Ну и самый маленький процент оттока у клиентов с низким балансом, он равен 12.5;
  • наибольший процент оттока у клиентов, принадлежащих к категории средней ЗП (80 тыс. до 300 тыс. рублей) он равен почти 20 от всего количества. Далее идет категория клиентов с высокой ЗП (свыше 300 тыс. рублей) для нее процент оттока равен 16.9. Ну и самый маленький процент оттока клиентов с низкой ЗП (до 80 тыс. рублей) он равен почти 15.

Высокооточные сегменты:¶

  • Клиенты в возрасте от 50 до 60 лет;
  • клиенты от 4 баллов собственности;
  • клиенты которые пользуются более 4 продуктами;
  • клиенты от 825 до 910 баллов кредитного скоринга;
  • клиенты со средним балансом (от 800000 до 4200000 рублей на счету);
  • клиенты со средней ЗП (от 80 тыс. до 300 тыс. рублей).

Сформированные высокооточные сегменты:¶

  • Клиенты мужского возраста из города Ярославль и не активный клиент. Процент оттока для данной категории равен почти 30, всего клиентов в данной категории 1490;
  • клиенты возрастной категории от 50 до 60 лет со балансом на счете от 80 тыс. до 4,2 млн. рублей. Процент оттока для данной категории равен почти 46, всего клиентов в данной категории 550;
  • клиенты с количеством продуктов более 2, без кредитной карты и с количеством баллов собственности более 5. Процент оттока для данной категории равен 39, всего клиентов в данной категории 880;

Портреты клиентов склонные к уходу из банка:¶

  1. Молодой человек мужского пола, с большим количеством продуктов банка.
  2. Клиент среднего возраста с большим количеством баллов кредитного скоринга, с балансом на счёте в диапазоне от 800000 до 4200000 рублей, и средней ЗП от 80 тыс. до 300 тыс. рублей.
  3. Клиент из города Ростова или Ярославля с большим количеством баллов собственности и редкой активностью.

Рекомендации:¶

  1. Необходимо анализировать последнюю активность клиента, и если последняя активность была более, например, месяца назад. То необходимо стараться завлечь клиента пользованием картой. К примеру, некими акциями или кэшбэком, или же беспроцентными переводами в течение месяца.
  2. Стараться предлагать больше различных акций и выгодных условий для клиентов молодого и среднего возраста;
  3. Необходимо проводить акции для клиентов, например, предлагать кредитные карты с бесплатным обслуживанием в течение года, при оформлении. Так как статистика показывает, что для клиентов, у которых оформлена кредитная карта, им менее свойственен отток.

Полученные выводы после проверки статистических гипотез:¶

  • Причина массового оттока клиентов заключается не в низком уровне зарплаты;
  • кредитный скоринг клиентов, которые остались, в среднем в 1.02 раза выше тех, кто ушел, что как показали результаты является значимым расхождением. Причина массового оттока клиентов заключается в менее выгодных условиях выплате кредитов. Скорее всего, что клиенты, которые ушли из банка решили рефинансировать свой кредит в другом банке. Поэтому необходимо смягчить условия кредитования, а также, возможно, предоставлять кредитные каникулы некоторым клиентам, подходящие по портреты, описанные ранее.